البرمجة المتفرقة
بسم الله الرحمن الرحيم
بعد ظهور لغات البرمجة القريبة من لغة الإنسان. ظهرت الحاجة إلى إيجاد وسيلة تسمح بـ:
هذه الوسيلة المبتكرة هي ملفات الكائنات ( Object Files ).
الفكرة بسيطة من وراء استعمال ملفات الكائنات وهي:
مكونات ملف الكائنات :
يحتوي ملف الكائنات على معلومات مختلفة تساعد برنامج الربط على أداء عمله بنجاح. من بينها:
الرموز المذكورة سابقا يمكن أن تكون أسماء متغيرات (Variables)، أسماء دوال(Functions) و إجراءات(Procedures) أو غيرها مثل البطاقات ( Labels ).
مكونات ملف التنفيذي للبرنامج (Executable File) :
يمكن اعتبار الملف التنفيذي كملف كائنات مخفف لأنه لا يحتوي إلا على بعض مكونات ملف الكائنات العادي.
الأمثلة التطبيقية:
يمكننا الآن تعلم كيفية استعمال برنامج تجميع
للأسمبلر + برنامج تجميع للغة C + برنامج ربط = لصناعة
برنامج تنفيذي بسيط لا يحتوي إلا على تعليمات الآلة + القيم الابتدائية و الثابتة
للمتغيرات.
سنحاول في الأمثلة التركيز على آلتين هما المعالج 8086 و
المعالج 386.
البرامج المستعملة هي:
| اسم الآلة | 8086 | 386 | ||
| نوع البرنامج | اسم البرنامج | رقم النسخة | اسم البرنامج | رقم النسخة |
| برنامج تجميع لغة C | bcc | 0.16.3 | gcc | 0.98.35 |
| برنامج تجميع لغة الأسمبلر | as86 | 0.16.3 | nasm | 3.3.1 |
| برنامج الربط | ld86 | 0.16.3 | ld | 2.14.90.5 |
رقم نسخة هذه البرامج غير مهم إلا في حالة عدم نجاح العملية التطبيقية. في هذه الحالة يرجى استعمال نسخة مماثلة أو أكبر . و الرجوع إلى ملفات المساعدة المرفقة بها.
الخطوات:
في كلا الآلتين 8086 و 386 سنحاول كتابة برنامج بلغة أسمبلر ينادي على دالة
بلغة C لحساب المجموع . يمكن التعبير عن برنامج الأسمبلر
الذي سيكتب على شكل التعليمة التالية بلغة
C:
| sum(15,10) |
الآلة 8086 :
هذه الآلة تستعمل سجلات ذات حجم
16 bit مثل DX,CX,BX,AX لذا يدعى البرنامج الذي ينفذ
عليها ببرنامج 16 bit.
نستعمل البرامج ld86 , bcc ,
as86 التي تنتج هذا النوع من البرامج (16 bit).
الملف "f1.s" :
| entry _start | تعريف البطاقة start_ على أنها رمز البداية لتعليمات الملف. |
| _start: | التصريح بالبطاقة start_ . |
|
mov ax,#10 push ax |
وضع العامل الثاني في السجل ax ثم تكويمه في الكومة. |
|
mov ax,#15 push ax |
وضع العامل الأول في السجل ax ثم تكويمه في الكومة. |
| call _sum | مناداة دالة المجموع sum. |
| add sp,#4 | استرجاع مكان تخزين العوامل في الكومة. ( 2 + 2 = 4 Byte مكان التخزين) |
| ret | نهاية البرنامج الصغير. |
| nop | أضيفت للحصول على 16 Byte. و هو اصغر حجم لقسم (Segment). |
أظن أنك لاحظت وجود الرمز "sum_" و ليس الرمز "sum" الذي يرمز للدالة المكتوبة بلغة C و المسماة "(int sum(int a,int b". هذا راجع إلى أن أغلب مجمعات اللغة C تحول اسم أي رمز بإضافة الحرف '_' في أوله لتلافي خطأ و جود اسم مشابه له في ملف مكتوب بلغة الأسمبلر.
هنالك أيضا التوجيه "entry _start" هو يهدف إلى الحصول على رمز بداية للملف . لكن هذا خاص فقط بالبرنامج bcc. عندما تنجح في الخطوات التالية جرب حذف هذا التوجيه من الملف و أعد الخطوات و ستجد أنك لا تنجح في العملية.
لصناعة الملف "f1.o" نكتب في سطر الأوامر ما يلي:
| as86 -u -o f1.o f1.s |
يمكنك التأكد من وجود الملف "f1.o" بالأمر "ls".
التوجيه "u-" يأمر البرنامج "as86" باعتبار أي رمز يستعمل داخل الملف "f1.s" و لم يصرح به داخله على أنه رمز خارجي. الرمز "sum_" هو رمز مستعمل و لكنه لم يصرح به. إذن فسيعتبره "as86" رمز خارجي.
التوجيه "o-" يحدد اسم الملف المنتج. في حالتنا اسمه "f1.o".
الملف "f2.c" :
نكتب الملف "f2.c" و هو يحتوي على دالة اسمها "sum"
تقوم بجمع عددين صحيحين ثم تعيد نتيجة المجموع إلى المنادي.
| int sum(int a,int b) { return a+b; } |
لصناعة الملف "f2.o" نكتب في سطر الأوامر ما يلي:
| bcc -ansi -c f2.c |
التوجيه "ansi-" يأمر "bcc" بالتقيد بلغة C المقررة من طرف المنظمة الأمريكية ANSI.
التوجيه "c-" يأمر "bcc" بصناعة ملف الكائنات "f2.o" فقط .
الملف "f3.bin" :
لربط الملفين المنتجين سابقا نستعمل البرنامج "ld86"
للحصول على الملف "f3.bin". نكتب في سطر الأوامر ما يلي :
| ld86 -d -o f3.bin f1.o f2.o |
من المهم جدا ترتيب ملفات الكائنات عند تنفيذ برنامج الربط. قمنا بربط الملف "f1.o" ثم الملف "f2.o".
التوجيه "d-" أمر "ld86" بصناعة ملف خالي من أي جداول رموز أو غيرها. بل يحتوي فقط على التعليمات و البيانات الموجودة في الملفين "f1.o" و "f2.o".
رؤية النتيجة:
الآن حصلنا على الملف "f3.bin". لرؤية محتواه بلغة الأسمبلر نكتب ما يلي في سطر الأوامر:
| ndisasm f3.bin |
فنحصل على :
| الانتقال |
التعليمة بالأساس 16 |
التعليمة بالاسمبلر | الوصف | الملف |
|
00000000 00000003 00000004 00000007 00000008 0000000B 0000000E 0000000F |
B80A00 50 B80F00 50 E80500 83C404 C3 90 |
mov ax,0xa push ax mov ax,0xf push ax call 0x10 add sp,byte +0x4 ret nop |
الملف f1.s مكتوب بصيغة
Nasm. في التعليمة "call 0x10" قام البرنامج "ld86" بتبديل الرمز "sum_" بالعدد 0x10 وهو العنوان الجديد للدالة "sum" في هذا الملف. لاحظ فقط السطر ذو الانتقال "00000010". |
الملف f1.s |
|
00000010 00000011 00000013 00000014 |
55 89E5 57 56 |
push bp mov bp,sp push di push si |
حفظ بعض السجلات. و التحضير لاستعمال السجل bp لاستخراج عوامل الدالة. |
الملف f2.c |
| 00000015 | 8B4604 | mov ax,[bp+0x4] | استرجاع العامل الأول من الكومة و وضعه داخل السجل ax. | |
| 00000018 | 034606 | add ax,[bp+0x6] | جمع العامل الأول مع الثاني | |
|
0000001B 0000001C 0000001D |
5E 5F 5D |
pop si pop di pop bp |
إعادة القيم السابقة لبعض السجلات | |
| 0000001E | C3 | ret | الرجوع إلى الإجراء المنادي | |
| 0000001F | 00 | db 0x00 | هذا الByte أضيف لملء الفراغ فقط. |
الآلة 386 :
هذه الآلة تستعمل سجلات ذات حجم
32 bit مثل EDX,ECX,EBX,EAX لذا يدعى البرنامج الذي
ينفذ عليها ببرنامج 32 bit.
نستعمل البرامج ld , gcc ,
nasm التي تنتج هذا النوع من البرامج (32
bit).
الملف "f1.s" :
هذا البرنامج مكتوب بصيغة "nasm". هي تختلف كثيرا عن
صيغة "as86".
| [bits 32] | التصريح بأن نوع البرنامج هو 32 bit. |
| extern sum | التصريح بأن الرمز sum هو رمز خارجي مستورد. |
| global _start | تعريف البطاقة start_ على أنها رمز مورّد خارج الملف. |
| _start: | التصريح بالبطاقة start_ . |
| push 10 | وضع العامل الثاني في الكومة مباشرة دون الحاجة إلى وسيط . هذه هي أحد ميزات الآلة 386 |
| push 15 | وضع العامل الأول في ي الكومة مباشرة. |
| call sum | مناداة دالة المجموع sum. |
| add esp,8 | استرجاع مكان تخزين العوامل في الكومة. ( 4 + 4 = 8 Byte مكان التخزين) |
| ret | نهاية البرنامج الصغير. |
أظن أنك لاحظت استعمال الرمز "sum" مباشرة و ليس "sum_" هذا لأن هذه النسخة المستعملة من "gcc" و النسخ التي تليها تترك الرموز على حالها. لكن النسخ الأقدم كانت تحوله بإضافة الحرف '_' في أول الرمز.
لصناعة الملف "f1.o" نكتب في سطر الأوامر ما يلي:
| nasm -f elf -o f1.o f1.s |
التوجيه "f elf-" يأمر البرنامج "nasm" بإنتاج ملف كائنات من نوع ELF المستعمل أيضا من طرف البرنامج "gcc". من الأفضل الحصول على ملفات كائنات من نفس النوع لربطها معا. لكن يمكن استعمال ملفات من أنواع مختلفة.
التوجيه "o-" يحدد اسم الملف المنتج. في حالتنا اسمه "f1.o".
الملف "f2.c" :
نكتب الملف "f2.c" و هو نفس الملف الذي استعملناه مع
8086. هنا تظهر جليا فائدة لغات
البرمجة المتطورة حيث أنها تسمح بإعادة استعمال نفس برنامج المصدر لصناعة برامج
تنفذ على آلات مختلفة.
| int sum(int a,int b) { return a+b; } |
لصناعة الملف "f2.o" نكتب في سطر الأوامر ما يلي:
| gcc -c f2.c |
التوجيه "c-" يأمر "gcc" بصناعة ملف الكائنات "f2.o" فقط .
الملف "f3.bin" :
لربط الملفين المنتجين سابقا نستعمل البرنامج "ld"
للحصول على الملف "f3.bin". نكتب في سطر الأوامر ما يلي :
| ld --oformat binary -o f3.bin -Ttext 0x0 f1.o f2.o |
من المهم جدا ترتيب ملفات الكائنات عند تنفيذ برنامج الربط. قمنا بربط الملف "f1.o" ثم الملف "f2.o".
التوجيه "oformat binary--" أمر "ld" بصناعة ملف خالي من أي جداول رموز أو غيرها. بل يحتوي فقط على التعليمات و البيانات الموجودة في الملفين "f1.o" و "f2.o".
التوجيه "Ttext-" يأمر "ld" بانتاج برنامج تبدأ تعليماته من العنوان 0x0.
رؤية النتيجة:
الآن حصلنا على الملف "f3.bin". لرؤية محتواه بلغة الأسمبلر نكتب ما يلي في سطر الأوامر:
| ndisasm -u f3.bin |
التوجيه "u-" يأمر "ndisasm" بتحليل البرنامج إلى تعليمات خاصة بـ 32 bit.
نحصل على :
|
الانتقال |
التعليمة بالأساس 16 |
التعليمة بالاسمبلر | الوصف | الملف |
|
00000000 00000005 0000000A 0000000F 00000015 00000016 00000017 |
680A000000 680F000000 E809000000 81C408000000 C3 90 90 |
push dword 0xa push dword 0xf call 0x18 add esp,0x8 ret nop nop |
الملف f1.s مكتوب بصيغة
Nasm. في التعليمة "call 0x18" قام البرنامج "ld" بتبديل الرمز "sum" بالعدد 0x18 وهو العنوان الجديد للدالة "sum" في هذا الملف. لاحظ فقط السطر ذو الانتقال "00000018". استعملت التعليمة nop لملأ الفراغ.و الحصول على عنوان للملف f2.o من مضاعفات العدد 4. |
الملف f1.s |
|
00000018 00000019 |
55 89E5 |
push ebp mov ebp,esp |
التحضير لاستعمال السجل ebp لاستخراج عوامل الدالة. |
الملف f2.c |
| 0000001B | 8B450C | mov eax,[ebp+0xc] | استرجاع العامل الثاني من الكومة و وضعه داخل السجل eax. | |
| 0000001E | 034508 | add eax,[ebp+0x8] | جمع العامل الأول مع الثاني. | |
| 00000021 | 5D | pop ebp | إعادة القيم السابقة للسجل epb. | |
| 00000022 | C3 | ret | الرجوع إلى الإجراء المنادي. |
من يريد المزيد:
يمكنك الآن التوسع في الموضوع و تطوير برامج بواسطة لغات برمجة مختلفة. و الاستفادة من برامجك السابقة. عند محاولة فعل ذلك ينبغي عليك دراسة التوجيهات المستعملة من قبل برامج الربط و مجمعات اللغات المراد استخدامها.
|
الكاتب : مراد حمود |
التاريخ : 15/12/2004 |